﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Diagnostics;
using System.Drawing;

namespace SpectationClient.DataBaseDescription {
   
    /// <summary>
    /// Class that reads a Binary Large Object (BLOB) (as byte[]) and returns some informations about,
    /// dependent on the datatype
    /// </summary>
    /// 
    public static class BLOBInfo {
        public enum BLOBType : byte{
            unknown = 0,
            //Raster Pictures
            JPG = 1,
            PNG = 2,
            TIFF = 3,
            BMP = 4,
            GIF = 5,
            EMF = 6,
            WMF = 7,
            //Geodata Files
            SHP = 8,
            //Compressed Files
            ZIP = 9,

            //Spectral Files
            ASD_FieldSpec = 10,

            //Envi Files
            envi_hdr = 11, //ENVI Header File => use ENVI_HeaderFileColumInfo for better determination

            //PDF
            PDF = 12
        }

        public enum SizeUnit : byte {
            Bytes = 0,
            KBytes = 1,
            MBytes = 2,
            GBytes = 3
        }

        //Graphic-Files
        private static byte[] ba_png = { 137, 80, 78, 71, 13, 10, 26, 10 };
        private static byte[] ba_jpg1 = { 0xFF, 0xD8, 0xFF, 0xE0 };
        private static byte[] ba_jpg2 = { 0xFF, 0xD8, 0xFF, 0xE1 };
        private static byte[] ba_tiff1 = { 0x49, 0x49, 0x2A };
        private static byte[] ba_tiff2 = { 0x4D, 0x4D, 0x2A };
        private static byte[] ba_bmp = { 0x42, 0x4D };
        private static byte[] ba_gif = { 0x47, 0x49, 0x46 };
        //GEO Files

        //Spectral Files
        private static byte[] ba_asd = {65,83,68}; // = "ASD";

        /// <summary>
        /// Return the BLOBType of a file.
        /// </summary>
        /// <param name="spectralFile"></param>
        /// <returns></returns>
        public static BLOBType getBLOBType(FileInfo fi) {
            return getBLOBType(fi.OpenRead());
        }

        /// <summary>
        /// Returns the BLOBType of a FileStream.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static BLOBType getBLOBType(Stream s) {
            byte[] ba = new byte[10];
            BinaryReader br = new BinaryReader(s);
            ba = br.ReadBytes(ba.Length);
            BLOBType bt = getBLOBType(ba);
            br.Close();
            s.Close();
            return bt;
        }

        public static BLOBType getBLOBType(System.Drawing.Image image) {
            return BLOBInfo.getBLOBType(Stuff.DataHelper.ImageToByteArray(image, image.RawFormat));
        }

        /// <summary>
        /// Returns the BLOBType of a byte array.
        /// </summary>
        /// <param name="imageBLOB"></param>
        /// <returns></returns>
        public static BLOBType getBLOBType(byte[] byteArray) {
            //Test for binary files
            if(EqualBytes(byteArray, ba_asd)) return BLOBType.ASD_FieldSpec;
            if(EqualBytes(byteArray, ba_bmp)) return BLOBType.BMP;
            if(EqualBytes(byteArray, ba_gif)) return BLOBType.GIF;
            if(EqualBytes(byteArray, ba_jpg1) ||
               EqualBytes(byteArray, ba_jpg2))return BLOBType.JPG;
            if(EqualBytes(byteArray, ba_png)) return BLOBType.PNG;
            if(EqualBytes(byteArray, ba_tiff1) ||
               EqualBytes(byteArray, ba_tiff2)) return BLOBType.TIFF;

            //Test for (non-binary) text files
            List<BLOBType> blobTypeList = new List<BLOBType>();
            blobTypeList.Add(getBLOBType(byteArray, Encoding.Default));
            blobTypeList.Add(getBLOBType(byteArray, Encoding.ASCII));
            blobTypeList.Add(getBLOBType(byteArray, Encoding.UTF7));
            blobTypeList.Add(getBLOBType(byteArray, Encoding.UTF8));
            blobTypeList.Add(getBLOBType(byteArray, Encoding.UTF32));
            blobTypeList.Add(getBLOBType(byteArray, Encoding.Unicode));
            blobTypeList.Add(getBLOBType(byteArray, Encoding.BigEndianUnicode));

            //return the first sucessful tested BLOBType
            foreach (BLOBType blobType in blobTypeList) {
                if (blobType != BLOBType.unknown) return blobType;
            }
            return BLOBType.unknown;
        }

        private static bool isEncodable(byte[] BA, Encoding encoding){
            bool ret = true;
            try{
            String s = encoding.GetString(BA);
            }catch(Exception ex){
                ret = false;
                Console.WriteLine("BLOBInfo.cs - isEncodable(): {0}",ex.Message);
            }
            return ret;
        }

        public static BLOBType getBLOBType(byte[] BA, Encoding E){
            if(isEncodable(BA, E)) return getBLOBType(E.GetString(BA));
            return BLOBType.unknown;
        }
        public static BLOBType getBLOBType(String textfile) {
            Regex r_envi_hdr = new Regex(@"^ENVI\s*$");
            Regex r_pdf = new Regex("^%PDF.*$");
            Char[] toSplit = {'\n'};
            String[] lines = textfile.Split(toSplit);
            if (r_envi_hdr.IsMatch(lines[0])) return BLOBType.envi_hdr;
            if (r_pdf.IsMatch(lines[0])) return BLOBType.PDF;

            return BLOBType.unknown;
        }
        /// <summary>
        /// Returns true if the given BLOBType is an image-Blobtype
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <returns></returns>
        public static bool isImage(FileInfo fi) {
            return isImage(getBLOBType(fi));
        }
        /// <summary>
        /// Returns true if the given BLOBType is an image-Blobtype
        /// </summary>
        /// <param name="imageBLOB"></param>
        /// <returns></returns>
        public static bool isImage(byte[] ba) {
            return isImage(getBLOBType(ba));
        }
        /// <summary>
        /// Returns true if the given BLOBType is an image-Blobtype
        /// </summary>
        /// <param name="blobType"></param>
        /// <returns></returns>
        public static bool isImage(BLOBType bt) {
            if (bt == BLOBType.BMP ||
                bt == BLOBType.EMF ||
                bt == BLOBType.GIF ||
                bt == BLOBType.JPG ||
                bt == BLOBType.PNG ||
                bt == BLOBType.TIFF ||
                bt == BLOBType.WMF) return true;
            return false;
        }

        public static double getSize(byte[] ba, SizeUnit su) {
            switch (su) { 
                case SizeUnit.KBytes: return ba.LongLength / 10^3;
                case SizeUnit.MBytes: return ba.LongLength / 10^6;
                case SizeUnit.GBytes: return ba.LongLength / 10^9;
            }
            return ba.LongLength;
        }

        public static string getSizeString(byte[] ba) {
            if (ba.LongLength < 1024) return ba.LongLength + "B";
            if (ba.LongLength < Math.Pow(10,6)) return (ba.LongLength / Math.Pow(10,3)) + "KB";
            if (ba.LongLength < Math.Pow(10,9)) return (ba.LongLength / Math.Pow(10,6)) + "MB";
            return (ba.LongLength / Math.Pow(10,9)).ToString() + "GB";
        }
        public static string getSizeString(byte[] ba, SizeUnit su) {
            String postfix = "Bytes";
            switch (su) {
                case SizeUnit.KBytes: postfix = "KB"; break;
                case SizeUnit.MBytes: postfix = "MB"; break;
                case SizeUnit.GBytes: postfix = "GB"; break;
            }
            return String.Format("{0} {1}", getSize(ba, su), postfix);
        }

        public static string getFileExtension(Image image) {
            return BLOBInfo.getFileExtension(BLOBInfo.getBLOBType(image));
        }

        public static string getFileExtension(BLOBType bt){
            switch (bt) {
                case BLOBType.ASD_FieldSpec: return "asd";
                case BLOBType.BMP: return "bmp";
                case BLOBType.EMF: return "emf";
                case BLOBType.GIF: return "gif";
                case BLOBType.JPG: return "jpg";
                case BLOBType.PNG: return "png";
                case BLOBType.SHP: return "shp";
                case BLOBType.TIFF: return "tiff";
                case BLOBType.WMF: return "wmf";
                case BLOBType.ZIP: return "zip";
                case BLOBType.envi_hdr: return "hdr";
                case BLOBType.unknown: return "";
                default: throw new Exception("unhandled BLOBType");
            }
        }


        public static string getFileExtension(Stream s) {
            return getFileExtension(getBLOBType(s));
        }

        public static string getFileExtension(byte[] ba) {
            if(ba == null) return null;
            return getFileExtension(getBLOBType(ba));
        }


        /// <summary>
        /// Returns true if first n-Bytes are pairwise equal. n = minY(byteArray1.Length, byteArray2.Length)
        /// </summary>
        /// <param name="byteArray1"></param>
        /// <param name="byteArray2"></param>
        /// <returns></returns>
        private static bool EqualBytes(byte[] byteArray1, byte[] byteArray2) {
            for (int i = 0; i < Math.Min(byteArray1.Length, byteArray2.Length); i++) {
                if (byteArray1[i] != byteArray2[i]) return false;
            }
            return true;
        }
    }
}
